using System;
using System.Collections;
using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;

namespace LBNHelpers
{
	public class I2CBus : IDisposable
	{
		#region Public properties

		public bool IsHardware 
		{ 
			get { return SoftDevice == null; }
		}

		public IEnumerable DeviceList
		{
			get { return _DeviceList; }
		}

		#endregion

		#region Public interface

		public I2CDevice CreateDevice(ushort address, int timeout)
		{
			if (_DeviceList.Contains(address))
			{
				return _DeviceList[address] as I2CDevice;
			}

			var dev = new I2CDevice(this, address, timeout);
			_DeviceList.Add(address, dev);

			return dev;
		}

		public void ScanI2CDevices()
		{
			var t = new Microsoft.SPOT.Hardware.I2CDevice.I2CTransaction[2];
			var regaddr = new byte[] { 0x00 };
			var data = new byte[8];

			t[0] = Microsoft.SPOT.Hardware.I2CDevice.CreateWriteTransaction(regaddr);
			t[1] = Microsoft.SPOT.Hardware.I2CDevice.CreateReadTransaction(data);

			if (IsHardware)
			{
				Debug.Print("Scanning Hardware I2C Bus for devices...\r\n");
			}
			else
			{
				Debug.Print("Scanning Software Bus for devices...\r\n");
			}

			for (ushort i = 1; i < 128; i++)
			{
				int s;
				if (SoftDevice != null) s = SoftDevice.Execute(i, new Microsoft.SPOT.Hardware.I2CDevice.I2CTransaction[1] { Microsoft.SPOT.Hardware.I2CDevice.CreateReadTransaction(new byte[1]) });
				else s = I2CManager.Execute(i, new Microsoft.SPOT.Hardware.I2CDevice.I2CTransaction[1] { Microsoft.SPOT.Hardware.I2CDevice.CreateReadTransaction(new byte[1]) });

				if (s == 1)
				{
					regaddr[0] = 0x00;

					if (IsHardware) I2CManager.Execute(i, t);
					else SoftDevice.Execute(i, t);

					string version = Convertion.ToString(data);

					regaddr[0] = 0x08;
					if (IsHardware) I2CManager.Execute(i, t);
					else SoftDevice.Execute(i, t);
					string vendor = Convertion.ToString(data);

					regaddr[0] = 0x10;
					if (IsHardware) I2CManager.Execute(i, t);
					else SoftDevice.Execute(i, t);
					string dev = Convertion.ToString(data);

					Debug.Print("Found at: 0x" + i.ToString());
					Debug.Print("  Version: " + version);
					Debug.Print("  Vendor : " + vendor);
					Debug.Print("  Device : " + dev);
					Debug.Print("");
				}
			}
		}

		#endregion

		#region Internal data members

		internal SoftwareI2C SoftDevice = null;

		internal Hashtable  _DeviceList = new Hashtable();

		#endregion

		#region Internal constructors

		internal I2CBus()
		{
		}

		internal I2CBus(Cpu.Pin scl, Cpu.Pin sda, int clock)
		{
			SoftDevice = new SoftwareI2C(scl, sda, clock);
		}

		#endregion

		public void Dispose()
		{
			if (SoftDevice != null)	SoftDevice.Dispose();

			foreach (DictionaryEntry entry in DeviceList)
			{
				I2CDevice device = entry.Value as I2CDevice;
				device.OwnerBus = null;
			}
		}

		#region Internal wrapper for I2C

		internal void ReadPort(ushort address, byte port, ref byte[] result, int timeout = 100)
		{
			if (IsHardware)
			{
				I2CManager.ReadPort(address, port, ref result, timeout);
			}

			else
			{
				I2CManager.ReadPort(SoftDevice, address, port, ref result, timeout);
			}
		}

		internal byte[] ReadPort(ushort address, byte port, byte sizetoread, int timeout = 100)
		{
			if (IsHardware)
			{
				return I2CManager.ReadPort(address, port, sizetoread, timeout);
			}
			else
			{
				return I2CManager.ReadPort(SoftDevice, address, port, sizetoread, timeout);
			}
		}

		internal byte ReadPortByte(ushort address, byte port, int timeout = 100)
		{
			if (IsHardware)
			{
				return I2CManager.ReadPortByte(address, port, timeout);
			}
			else
			{
				return I2CManager.ReadPortByte(SoftDevice, address, port, timeout);
			}
		}

		internal int WritePort(ushort address, byte port, byte data, int timeout = 100)
		{
			if (IsHardware)
			{
				return I2CManager.WritePort(address, port, data, timeout);
			}
			else
			{
				return I2CManager.WritePort(SoftDevice, address, port, data, timeout);
			}
		}

		internal int WritePort(ushort address, byte port, byte[] data, int timeout = 100)
		{
			if (IsHardware)
			{
				return I2CManager.WritePort(address, port, data, timeout);
			}
			else
			{
				return I2CManager.WritePort(SoftDevice, address, port, data, timeout);
			}
		}

		#endregion
	}
}